package ilender.facebook;

import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Arrays;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;

import lombok.*;

import com.google.gson.*;
import org.apache.commons.lang3.StringUtils;

import ilender.util.*;
import ilender.exceptions.*;

/**
 * Entry point for interfacing with Facebook's Graph API.
 */
public final class GraphAPI {
	private static GraphAPI instance = null;

	public final String accessTokenEncoded;

	public final FBApplication thisApplication;

	/** Constructor. Connects to Facebook to obtain access token. */
	private GraphAPI() {
		Properties fbProps = PropertiesTools.fetchFacebookProperties();
		try {
			val rawResult=HttpTools.requestGet(
					String.format(
						"https://graph.facebook.com/oauth/access_token?client_id=%s&client_secret=%s&grant_type=client_credentials",
						fbProps.getProperty("app-id"),
						fbProps.getProperty("app-secret")));
			val accessTokenValue=rawResult.split("=", 2)[1].trim();
			this.accessTokenEncoded=URLEncoder.encode(accessTokenValue,"UTF8");
			this.thisApplication=new FBApplication(this,Long.parseLong(fbProps.getProperty("app-id")),fbProps.getProperty("app-namespace"));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	/** Exactly what it says on tin. */
	public static GraphAPI getInstance() {
		if (null == instance)
			instance = new GraphAPI();
		return instance;
	}

	/**
	 * Retreives data on the logged in user.
	 *
	 * @param userId
	 *            The id of the user to retreive data on.
	 * @param fields
	 *            The data fields to retrieve.
	 * @return A JSON object containing the data.
	 */
	public JsonObject getUserData(String userId, String... fields) {
		return HttpTools
				.requestGetJson(
						String.format(
								"https://graph.facebook.com/%s?fields=%s&access_token=%s",
								userId, StringUtils.join(fields, ","),
								accessTokenEncoded)).getAsJsonObject();
	}

	public JsonObject getUserData(long userId, String... fields) {
		return getUserData(String.format("%s", userId), fields);
	}

	/**
	 * Performs a facebook Batch request.
	 *
	 * @param request
	 *            The JSON array of requests to send to Facebook.
	 * @return The list of results.
	 */
	public List<JsonElement> batchRequest(JsonArray request) {
		List<JsonElement> result = new LinkedList<JsonElement>();
		JsonParser parser = new JsonParser();
		for (JsonElement entry : HttpTools.requestPostJson(
				"https://graph.facebook.com", "access_token",
				accessTokenEncoded, "batch", request.toString())
				.getAsJsonArray()) {
			result.add(parser.parse(entry.getAsJsonObject().get("body")
					.getAsString()));
		}
		return result;
	}

	/**
	 * Performs a single post request.
	 *
	 * @param path
	 *            The path of the request.
	 * @param fieldArgs
	 *            The post fields.
	 * @return The result of the request.
	 */
	public JsonElement makePostRequest(String path, String... fieldArgs) {
		String[] newFieldArgs = Arrays.copyOf(fieldArgs, fieldArgs.length + 2);
		newFieldArgs[newFieldArgs.length - 2] = "access_token";
		newFieldArgs[newFieldArgs.length - 1] = accessTokenEncoded;
		String url = "https://graph.facebook.com/" + path;
		JsonElement result = HttpTools.requestPostJson(url, newFieldArgs);
		System.out.println(result);
		if (result.isJsonObject() && result.getAsJsonObject().has("error")) {
			JsonObject error = result.getAsJsonObject().get("error")
					.getAsJsonObject();
			switch (error.get("type").getAsString()) {
			case "OAuthException":
				throw new UnauthorizedGraphActionException(error.get("message")
						.getAsString(), url);
			}
		}
		return result;
	}

	/**
	 * Performs a single get request.
	 *
	 * @param path
	 *            The path of the request.
	 * @param fieldArgs
	 *            The get fields.
	 * @return The result of the request.
	 */
	public JsonElement makeGetRequest(String path, String... fieldArgs) {
		StringBuilder urlBuilder=new StringBuilder();
		urlBuilder.append("https://graph.facebook.com/");
		urlBuilder.append(path);
		urlBuilder.append("?access_token=");
		urlBuilder.append(accessTokenEncoded);
		try{
			for(int i=0;i<fieldArgs.length;i+=2){
				urlBuilder.append('&');
				urlBuilder.append(URLEncoder.encode(fieldArgs[i],"UTF8"));
				urlBuilder.append('=');
				urlBuilder.append(URLEncoder.encode(fieldArgs[i+1],"UTF8"));
			}
		}catch(UnsupportedEncodingException e){}
		return HttpTools.requestGetJson(urlBuilder.toString());
	}

	/**
	 * Returns a Facebook user handler.
	 *
	 * @param id
	 *            The Facebook id of the user.
	 * @return The handler.
	 */
	public FBUser getFBUser(long id) {
		return new FBUser(this, id);
	}

	public FBUser getFBUser(String idAsString) {
		return getFBUser(Long.valueOf(idAsString));
	}
}
